package com.yami.shop.security.api.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.bean.model.User;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.Json;
import com.yami.shop.common.util.PrincipalUtil;
import com.yami.shop.dao.UserMapper;
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
import com.yami.shop.security.common.config.WxConfig;
import com.yami.shop.security.common.config.WxHttpUtil;
import com.yami.shop.security.common.dto.AuthenticationDTO;
import com.yami.shop.security.common.dto.WxAuthorizeLoginMiniReqVO;
import com.yami.shop.security.common.dto.WxAuthorizeResponseVO;
import com.yami.shop.security.common.dto.WxPhoneVO;
import com.yami.shop.security.common.enums.SysTypeEnum;
import com.yami.shop.security.common.manager.PasswordCheckManager;
import com.yami.shop.security.common.manager.PasswordManager;
import com.yami.shop.security.common.manager.TokenStore;
import com.yami.shop.security.common.util.AuthUserContext;
import com.yami.shop.security.common.vo.TokenInfoVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.validation.Valid;
import java.security.spec.AlgorithmParameterSpec;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;

/**
 * @author 菠萝凤梨
 * @date 2022/3/28 15:20
 */
@RestController
@Api(tags = "登录")
@Slf4j
public class LoginController {
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordCheckManager passwordCheckManager;

    @Autowired
    private PasswordManager passwordManager;

    @PostMapping("/login")
    @ApiOperation(value = "账号密码(用于前端登录)", notes = "通过账号/手机号/用户名密码登录，还要携带用户的类型，也就是用户所在的系统")
    public ResponseEntity<TokenInfoVO> login(
            @Valid @RequestBody AuthenticationDTO authenticationDTO) {
        String mobileOrUserName = authenticationDTO.getUserName();
        User user = getUser(mobileOrUserName);

        String decryptPassword = passwordManager.decryptPassword(authenticationDTO.getPassWord());

        // 半小时内密码输入错误十次，已限制登录30分钟
        passwordCheckManager.checkPassword(SysTypeEnum.ORDINARY,authenticationDTO.getUserName(), decryptPassword, user.getLoginPassword());

        UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
        userInfoInToken.setUserId(user.getUserId());
        userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
        userInfoInToken.setEnabled(user.getStatus() == 1);
        // 存储token返回vo
        TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
        tokenInfoVO.setUser(user);
        return ResponseEntity.ok(tokenInfoVO);
    }

    private User getUser(String mobileOrUserName) {
        User user = null;
        // 手机验证码登陆，或传过来的账号很像手机号
        if (PrincipalUtil.isMobile(mobileOrUserName)) {
            user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, mobileOrUserName));
        }
        // 如果不是手机验证码登陆， 找不到手机号就找用户名
        if  (user == null) {
            user = userMapper.selectOneByUserName(mobileOrUserName);
        }
        if (user == null) {
            throw new YamiShopBindException("账号或密码不正确");
        }
        return user;
    }


    @PostMapping("p/wx/asyncPhone")
    @ApiOperation("【PUBLIC】-【COMMON】-【微信同步手机号】-【HZL】")
    public ResponseEntity<TokenInfoVO> asyncPhone(@RequestBody @Validated WxAuthorizeLoginMiniReqVO reqVO) {
        String encryptedData = reqVO.getEncryptedData();
        String iv = reqVO.getIv();

        User userInfo = userMapper.selectById(AuthUserContext.get().getUserId());

        // 1.请求微信官方换取openid

        String session_key = redisTemplate.opsForValue().get(userInfo.getOpenId());
        // 2.解析密文换取手机号
        String result = decode(encryptedData, iv, session_key);
        log.info("result=" + result);
        if (StringUtils.isEmpty(result)) {
            throw new YamiShopBindException("解析密文换取手机号失败");
        }

        // 3.to bean
        WxPhoneVO phoneVO = Json.parseObject(result, WxPhoneVO.class);
        String phone = phoneVO.getPurePhoneNumber();
        userInfo.setUserMobile(phone);
        userMapper.updateById(userInfo);
        return ResponseEntity.ok(null);
    }

    @PostMapping("wx/login")
    @ApiOperation("【PUBLIC】-【COMMON】-【微信授权登录】-【HZL】")
    public ResponseEntity<TokenInfoVO> residentLoginWxByAuthorize(@RequestBody @Validated WxAuthorizeLoginMiniReqVO reqVO) {
        String jsCode = reqVO.getJsCode();
        String encryptedData = reqVO.getEncryptedData();
        String iv = reqVO.getIv();

        if(jsCode != null){
        }

        // 1.请求微信官方换取openid
        WxAuthorizeResponseVO responseVO = authorize(jsCode);
        if (StringUtils.isEmpty(responseVO.getOpenid()) || StringUtils.isEmpty(responseVO.getSession_key())) {
//            responseVO = getWxAuthorizeResponseVO();
            throw new YamiShopBindException("获取信息失败");
        }
        redisTemplate.opsForValue().set(responseVO.getOpenid(),responseVO.getSession_key());

/*        // 2.解析密文换取手机号
        String result = decode(encryptedData, iv, responseVO.getSession_key());
        log.info("result=" + result);
        if (StringUtils.isEmpty(result)) {
            throw new YamiShopBindException("解析密文换取手机号失败");
        }*/

        // 3.to bean
//        WxPhoneVO phoneVO = Json.parseObject(result, WxPhoneVO.class);
//        String phone = phoneVO.getPurePhoneNumber();

        // 根据微信openID查询
        User  user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, responseVO.getOpenid()));
        Date now = new Date();
        if(user == null){
            // 需要插入

            user = new User();
            user.setModifyTime(now);
            user.setUserRegtime(now);
            user.setStatus(1);
            user.setNickName(reqVO.getNickname());
            user.setPic(reqVO.getAvatarUrl());
            user.setOpenId(responseVO.getOpenid());
            String userId = IdUtil.simpleUUID();
            user.setUserId(userId);
            userMapper.insert(user);
        }else {
            user.setModifyTime(now);
            user.setNickName(reqVO.getNickname());
            user.setPic(reqVO.getAvatarUrl());
            userMapper.updateById(user);
        }

        // 2. 登录
        UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO();
        userInfoInTokenBO.setUserId(user.getUserId());
        userInfoInTokenBO.setSysType(SysTypeEnum.ORDINARY.value());
        userInfoInTokenBO.setIsAdmin(0);
        userInfoInTokenBO.setEnabled(true);
        TokenInfoVO body = tokenStore.storeAndGetVo(userInfoInTokenBO);
        body.setUser(user);
        return ResponseEntity.ok(body);
    }

    @Autowired
    private  RedisTemplate<String, String> redisTemplate;

    @Autowired
    private WxConfig wxConfig;

    private final String grantType = "authorization_code";

    @Autowired
    private WxHttpUtil wxHttpUtil;

    public WxAuthorizeResponseVO authorize(String jsCode) {
        StringBuffer buffer = new StringBuffer();
        buffer.append(wxConfig.getMiniLoginUrl());
        buffer.append("?");
        buffer.append("appid=").append(wxConfig.getAppId()).append("&");
        buffer.append("secret=").append(wxConfig.getAppsecret()).append("&");

        buffer.append("js_code=").append(jsCode).append("&");
        buffer.append("grant_type=").append(grantType);
        String url = buffer.toString();
        WxAuthorizeResponseVO responseVO = null;
        LocalDateTime localDateTime = LocalDateTime.now();
        try {
            responseVO = wxHttpUtil.wxGet(url, null, WxAuthorizeResponseVO.class);
        } catch (Exception e) {
            log.error("微信小程序获取openid请求异常");
            e.printStackTrace();
        } finally {
            log.info("[请求日志]-{\"url\":\"{}\",\"method\":\"{}\",\"request\":{},\"response\":{},\"startTime\":\"{}\",\"endTime\":\"{}\"}",
                    url, "GET", null, responseVO,
                    localDateTime.toString().replace("T", " "),
                    LocalDateTime.now().toString().replace("T", " "));
        }
        return responseVO;
    }


    public String decode(String encrypdata, String ivdata, String sessionKey) {
        try {
            byte[] encrypData = Base64.decodeBase64(encrypdata);
            byte[] ivData = Base64.decodeBase64(ivdata);
            byte[] sessionKeyByte = Base64.decodeBase64(sessionKey);
            AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyByte, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            //解析解密后的字符串
            String phoneJson = new String(cipher.doFinal(encrypData), "UTF-8");
            return phoneJson;
        } catch (Exception e) {
            log.error("微信小程序解密出现异常");
            e.printStackTrace();
        }
        return null;
    }

}
